package com.partner.partnerback.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.partner.partnerback.common.Constants;
import com.partner.partnerback.common.enums.EmailCodeEnum;
import com.partner.partnerback.controller.domain.LoginDTO;
import com.partner.partnerback.controller.domain.UserRequest;
import com.partner.partnerback.entity.User;
import com.partner.partnerback.exception.ServiceException;
import com.partner.partnerback.mapper.UserMapper;
import com.partner.partnerback.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.partner.partnerback.utils.EmailUtils;
import com.partner.partnerback.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 程序员曾庆红
 * @since 2023-04-12
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    // key 是code value 是时间戳
    //private static final Map<String, Long> CODE_MAP = new ConcurrentHashMap<>();

    private static final long TIME_IN_MS5 = 5*60*1000; //五分钟毫秒数

    @Resource
    private EmailUtils emailUtils;

    @Override
    public LoginDTO login(UserRequest user) {
        User dbUser = null;
        try{
            dbUser = getOne(new UpdateWrapper<User>().eq("username",user.getUsername())
                    .or()
                    .eq("email",user.getUsername()));
        }catch(Exception e){
            throw new RuntimeException("系统错误");
        }
        if (dbUser == null){
            throw new ServiceException("不存在此用户");
        }
//        String securePass = SaSecureUtil.aesEncrypt(Constants.PASSWORD_KEY, user.getPassword());
//        if (!dbUser.getPassword().equals(securePass)){
//            throw new ServiceException("用户名或密码错误");
//        }
        if (!BCrypt.checkpw(user.getPassword(),dbUser.getPassword())){
            throw new ServiceException("用户名或密码错误");
        }
        //登录
        StpUtil.login(dbUser.getUid());
        StpUtil.getSession().set(Constants.LOGIN_USER_INFO,dbUser);
        String tokenValue = StpUtil.getTokenInfo().getTokenValue();
        LoginDTO loginDTO = LoginDTO.builder().user(dbUser).token(tokenValue).build();
        return loginDTO;
    }

    @Override
    public User register(UserRequest user) {
        String key = Constants.EMAIL_CODE+EmailCodeEnum.REGISTER.getValue()+user.getEmail();
        validateEmail(key,user.getEmailCode());
        try{
            //存储user
            User saveUser = new User();

            BeanUtils.copyProperties(user,saveUser);
            User isSaveUser = saveUser(saveUser);
            return isSaveUser;
        }catch(Exception e){
            throw new RuntimeException("系统错误",e);
        }

    }

    @Override
    public String passwordReset(UserRequest userRequest) {
        String email = userRequest.getEmail();
        User dbUser = getOne(new UpdateWrapper<User>().eq("email", email));
        if (dbUser == null){
            throw new ServiceException("未找到用户");
        }
        //校验邮箱验证码
        String key = Constants.EMAIL_CODE+EmailCodeEnum.RESET_PASSWORD.getValue()+email;
        validateEmail(key, userRequest.getEmailCode());
        String newPass = "123";
        dbUser.setPassword(BCrypt.hashpw(newPass));
        try{
            updateById(dbUser);
        }catch(Exception e){
            throw new RuntimeException("修改失败",e);
        }

        return newPass;
    }

    @Override
    public void sendEmail(String email, String type) {
        if (email == null || "".equals(email)){
            throw new ServiceException("邮箱不能为空");
        }
        
        String emailPrefix = EmailCodeEnum.getValue(type);
        if (StrUtil.isBlank(emailPrefix)){
            throw new ServiceException("不支持的验证类型");
        }

        String key = Constants.EMAIL_CODE + emailPrefix + email;
        Long expireTime = RedisUtils.getExpireTime(key);
        //如果过期时间大于3分钟 不允许发送
        if (expireTime != null && expireTime > 3 * 60L){
            throw new ServiceException("发送邮箱验证过于频繁");
        }

        String code = RandomUtil.randomString(6);
        log.info("本次验证code="+code);
        String context = "<b>尊敬的用户：</b><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;您好，" +
                "Partner交友网提醒您本次的验证码是：<b>{}</b>, "+"有效期5分钟。<br><br><br><b>Partner交友网</b>";
        String html = StrUtil.format(context, code);
        User user = getOne(new UpdateWrapper<User>().eq("email", email));
        if (EmailCodeEnum.REGISTER.getValue().equals(emailPrefix)){ //无需权限验证即可发送邮箱验证码
            if (user != null){

                throw new ServiceException("邮箱已经注册");
            }
        }else if(EmailCodeEnum.RESET_PASSWORD.getValue().equals(emailPrefix)){
            if (user == null){
                throw new ServiceException("未找到用户");
            }
        }

        ThreadUtil.execAsync(() -> { //多线程 执行异步请求
            emailUtils.sendHtml("【Partner交友网】邮箱验证提醒",html,email);

            RedisUtils.setCacheObject(key, code, TIME_IN_MS5, TimeUnit.MILLISECONDS);
        });

    }

    /**
     * 校验邮箱
     * @param emailCode
     */
    private void validateEmail(String emailKey, String emailCode){
        // 校验邮箱
        String code = RedisUtils.getCacheObject(emailKey);
        if (StrUtil.isBlank(code)){
            throw new ServiceException("验证码失效");
        }
        if (!emailCode.equals(code)){
            throw new ServiceException("验证码错误");
        }

        RedisUtils.deleteObject(emailKey);
    }

    private User saveUser(User user){
        User dbUser = getOne(new UpdateWrapper<User>().eq("username",user.getUsername()));
        if (dbUser != null){
            throw new ServiceException("此用户已存在");
        }
        //设置昵称
        if (StrUtil.isBlank(user.getName())){
            user.setName(Constants.USER_NAME_PREFIX+ RandomUtil.randomString(4));
        }
        //设置默认密码
        if (StrUtil.isBlank(user.getPassword())){
            user.setPassword("123");
        }
        //加密用户密码
        //user.setPassword(SaSecureUtil.aesEncrypt(Constants.PASSWORD_KEY, user.getPassword()));

        user.setPassword(BCrypt.hashpw(user.getPassword()));

        user.setUid(IdUtil.fastSimpleUUID());
        try{
            save(user);
        }catch (Exception e){
            throw new RuntimeException("注册失败", e);
        }
        return user;
    }

}
